// Copyright 2012 Anno Langen. All Rights Reserved.

package org.anno;

import com.google.common.base.Preconditions;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Module;
import com.google.inject.Provides;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

import javax.inject.Singleton;

/**
 * The collection of all movies.
 */
public class Movies {

  public static final int MAX_ID = 17770;
  final Movie[] values;

  public Movies(Movie[] values) {
    this.values = values;
  }

  public static Module getModule() {
    return new AbstractModule() {
      protected void configure() {
      }

      @Provides @Singleton Movies movies(Ratings ratings)
          throws IOException, ClassNotFoundException, InterruptedException {
        return new File("movies.jos").exists() ? read()
            : readFirstTimeAndSave("movies.jos", ratings);
      }
    };
  }

  private static Movies read() throws ClassNotFoundException, IOException {
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream("movies.jos"));
    try {
      return new Movies((Movie[]) ois.readObject());
    } finally {
      ois.close();
    }
  }

  public static void main(String[] args) throws IOException {
    Guice.createInjector(Ratings.getModule(), Movies.getModule()).getInstance(Movies.class);
    Parallel.EXECUTOR.shutdown();
  }

  private static Movies readFirstTimeAndSave(String objectStreamFile, Ratings ratings)
      throws IOException, InterruptedException {
    Movies movies = initialBuild(ratings, "../download/movie_titles.txt");
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(objectStreamFile));
    oos.writeObject(movies.values);
    oos.close();
    return movies;
  }

  private static Movies initialBuild(Ratings ratings, String titleFile)
      throws InterruptedException, IOException {
    final int[] sum = new int[MAX_ID + 1];
    final int[] count = new int[MAX_ID + 1];

    ratings.sendByMovie(new Ratings.ProcessorFactory<Ratings.MovieProcessor>() {
      public Ratings.MovieProcessor get(final int id) {
        return new Ratings.MovieProcessor() {
          public void userScore(int user, int score) {
            sum[id] += score;
            count[id] += 1;
          }

          public void close() {
          }
        };
      }
    });
    BufferedReader reader = new BufferedReader(new FileReader(titleFile));
    Movie[] movies = new Movie[MAX_ID];
    for (int i = 0; i < movies.length; i++) {
      String line = Preconditions.checkNotNull(reader.readLine(), "no title for %s", i);
      int delim = line.indexOf(',');
      int id = Integer.parseInt(line.substring(0, delim));
      Preconditions.checkArgument(id == i + 1,
          "misaligned title file. expected " + (i + 1) + " but got " + line);
      movies[i] = new Movie(id, line.substring(delim + 6), count[id], sum[id]);
    }
    return new Movies(movies);
  }

  double getMeanScore(int movie) {
    return values[movie - 1].getMeanScore();
  }
}
